package com.tencent.tsf.gateway.zuul1.filter.pre;

import static com.tencent.tsf.gateway.core.constant.GatewayConstant.TSF_GATEWAY_FILTER_CHAIN;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.RETRYABLE_KEY;

import com.netflix.zuul.context.RequestContext;
import com.tencent.tsf.gateway.core.AuthCheckUtil;
import com.tencent.tsf.gateway.core.GatewayEngine;
import com.tencent.tsf.gateway.core.GatewayRequestContext;
import com.tencent.tsf.gateway.core.TsfGatewayRequest;
import com.tencent.tsf.gateway.core.constant.GatewayConstant;
import com.tencent.tsf.gateway.core.constant.HeaderName;
import com.tencent.tsf.gateway.core.exception.TsfGatewayError;
import com.tencent.tsf.gateway.core.exception.TsfGatewayException;
import com.tencent.tsf.gateway.zuul1.filter.TsfGatewayZuulFilter;

import java.lang.invoke.MethodHandles;
import java.net.URI;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.util.StringUtils;

public class TsfGatewayAuthPreFilter extends TsfGatewayZuulFilter {

    private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return Integer.MIN_VALUE + 100;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        String traceId = ctx.getRequest().getHeader(HeaderName.TRACE_ID);
        if (StringUtils.isEmpty(traceId)) {
            traceId = UUID.randomUUID().toString();
        }
        ctx.set(HeaderName.TRACE_ID, traceId);
        ctx.set(RETRYABLE_KEY, false);
        GatewayEngine gatewayEngine = GatewayEngine.getEngine();
        //设置NodeId
        if (gatewayEngine.getGatewayConfig() != null && !StringUtils.isEmpty(gatewayEngine.getGatewayConfig().getNodeId())) {
            ctx.set(HeaderName.NODE, gatewayEngine.getGatewayConfig().getNodeId());
        }
        ctx.set(GatewayConstant.GatewayContext.GATEWAY_REQUEST_START_TIME, System.currentTimeMillis());
        return ctx.getBoolean(TSF_GATEWAY_FILTER_CHAIN, true);
    }

    @Override
    public Object run() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest servletRequest = requestContext.getRequest();
        String requestUri = (String) requestContext.get(FilterConstants.REQUEST_URI_KEY);
        if (Strings.isBlank(requestUri)) {
            requestUri = servletRequest.getRequestURI();
        }

        TsfGatewayRequest tsfGatewayRequest = new TsfGatewayRequest();
        tsfGatewayRequest.setMethod(servletRequest.getMethod());

        try {
            tsfGatewayRequest.setParameterMap(servletRequest.getParameterMap());
            tsfGatewayRequest.setUri(new URI(requestUri));
        } catch (Throwable t) {
            logger.error("[tsf-gateway] url parse error. uri: " + requestUri, t);
            throw new TsfGatewayException(TsfGatewayError.GATEWAY_INTERNAL_ERROR, "URLParse Error");
        }
        requestContext.set(GatewayConstant.GatewayContext.GATEWAY_REQUEST_URI, requestUri);
        tsfGatewayRequest.setRequestHeaders(copyRequestHeader(requestContext));
        tsfGatewayRequest.setHeaders(buildTsfGatewayHeaderMap(requestContext));
        tsfGatewayRequest.setCookieMap(getCookieMap(servletRequest));
        GatewayRequestContext gatewayRequestContext = AuthCheckUtil.preCheck(tsfGatewayRequest);
        gatewayRequestContext.setGatewayRequest(tsfGatewayRequest);
        requestContext.set(GatewayConstant.GatewayContext.GATEWAY_REQUEST_CONTEXT, gatewayRequestContext);
        return null;
    }

    private HashMap<String, String> getCookieMap(HttpServletRequest servletRequest) {
        Cookie[] cookies = servletRequest.getCookies();
        if (cookies == null) {
            return new HashMap<>(0);
        }
        HashMap<String, String> cookieMap = new HashMap<>(cookies.length);
        for (Cookie cookie : cookies) {
            cookieMap.put(cookie.getName(), cookie.getValue());
        }
        return cookieMap;
    }

    private Map<String, String> buildTsfGatewayHeaderMap(RequestContext requestContext) {
        HttpServletRequest servletRequest = requestContext.getRequest();
        Map<String, String> headerMap = new LinkedHashMap<>();
        headerMap.put(HeaderName.NODE, servletRequest.getHeader(HeaderName.NODE));
        headerMap.put(HeaderName.NONCE, servletRequest.getHeader(HeaderName.NONCE));
        headerMap.put(HeaderName.APP_KEY, servletRequest.getHeader(HeaderName.APP_KEY));
        headerMap.put(HeaderName.ALG, servletRequest.getHeader(HeaderName.ALG));
        headerMap.put(HeaderName.SIGN, servletRequest.getHeader(HeaderName.SIGN));
        headerMap.put(HeaderName.TRACE_ID, StringUtils.isEmpty(servletRequest.getHeader(HeaderName.TRACE_ID)) ?
                String.valueOf(requestContext.get(HeaderName.TRACE_ID)) : servletRequest.getHeader(HeaderName.TRACE_ID));
        return headerMap;
    }

    private Map<String, String> copyRequestHeader(RequestContext requestContext) {
        HttpServletRequest request = requestContext.getRequest();
        Map<String, String> headerMap = new LinkedHashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            headerMap.put(headerName, headerValue);
        }
        return headerMap;
    }

}
